#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <cstdio>
#include "Thread.hpp"
#include "LockGuard.hpp"

// 应用方的视角
std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}

class ThreadData
{
public:
    ThreadData(const std::string &name, pthread_mutex_t *lock)
    : threadname(name), pmutex(lock)
    {}
public:
    std::string threadname;
    pthread_mutex_t *pmutex;
};

int ticket = 10000;
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

// 加锁：
// 1. 我们要尽可能的给少的代码块加锁
// 2. 一般加锁，都是给临界区加锁
// 3. 个别系统，抢票代码会出现很多的票被同一个线程抢完了，也就是说有的线程抢的特别多，有的少
// 4. 多线程运行，同一份资源，有线程长时间无法拥有资源，但是有的却一直拥有，这就是饥饿问题
// 5. 要解决饥饿问题，要让线程执行的时候，具备一定的顺序性 --- 同步


//int myerrno = 0;
void GetTicket(ThreadData *td)
{
    // int localerrno = myerrno;//这里保护了全局变量myerrno，线程是安全的
    // ......
    // myerrno = 2;
    // ......
    // myerrno = localerrno;
    

    //函数状态随着函数被调用而发生改变的函数
    // static int cnt = 0;
    // cnt++;
    //----------------

    while (true)
    {
        pthread_mutex_lock(td->pmutex);
        {//用花括号括起来表示这里面是临界区代码
            // LockGuard lockguard(td->pmutex);//在这里定义局部变量会调用构造函数加锁，等这个变量销毁时自动调用析构函数进行解锁
            if (ticket > 0) 
            {
                usleep(1000);
                printf("%s get a ticket: %d\n", td->threadname.c_str(), ticket);
                ticket--;
                // pthread_mutex_unlock(&mutex);
                pthread_mutex_unlock(td->pmutex); //这里如果不小心写成了加锁，那么就会死锁，也就是说一把锁也可能死锁
            }
            else
            {
                // pthread_mutex_unlock(&mutex);
                pthread_mutex_unlock(td->pmutex); //
                break;
            }
        }
        // 实际情况，还有后续的动作， TODO?
    }
}

int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);

    std::string name1 = GetThreadName();
    ThreadData *td = new ThreadData(name1, &mutex);
    Thread<ThreadData *> t1(name1, GetTicket, td);

    std::string name2 = GetThreadName();
    ThreadData *td2 = new ThreadData(name2, &mutex);
    Thread<ThreadData *> t2(name2, GetTicket, td2);

    std::string name3 = GetThreadName();
    ThreadData *td3 = new ThreadData(name3, &mutex);
    Thread<ThreadData *> t3(name3, GetTicket, td3);

    std::string name4 = GetThreadName();
    ThreadData *td4 = new ThreadData(name4, &mutex);
    Thread<ThreadData *> t4(name4, GetTicket, td4);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    pthread_mutex_destroy(&mutex);
    delete td;
    delete td2;
    delete td3;
    delete td4;

    return 0;
}